package md.mru.frontend.beans;

import md.mru.backend.core.dtos.ClasificationDto;
import md.mru.backend.core.dtos.GroupDto;
import md.mru.backend.core.dtos.TreeNodeModelDto;
import md.mru.backend.core.service.AdministrationService;
import md.mru.backend.core.service.TreeService;
import org.primefaces.event.TreeDragDropEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import java.util.ArrayList;
import java.util.List;

/**
 * @author sergiu.marin
 * @since 12.04.2014.
 */
@ManagedBean(name = "treeAdministrationBean")
@SessionScoped
public class TreeAdministrationBean extends BackingBean {

    private static final long serialVersionUID = 1612454877681660019L;

    private TreeNode rootModel;
    private TreeNode selectedTreeNode;
    private TreeNode[] selectedTreeNodes;
    private boolean treeLoaded = false;
    @ManagedProperty(value = "#{treeService}")
    private TreeService treeService;
    @ManagedProperty(value = "#{administrationService}")
    private AdministrationService administrationService;
    private TreeNodeModelDto treeNode;
    private boolean moveActivated;
    private boolean administrationActivated;
    private List<ClasificationDto> treeNodeTypes;
    private List<GroupDto> groups;
    private long selectedGroupId;

    @PostConstruct
    public void init() {
        this.loadTree();
    }

    private void loadTree() {
        updateTree();
        treeLoaded = true;
        //Thread thr = new Thread(run);
        //thr.start();
    }

    public void updateSelection() {

    }

    /*public Runnable run = new Runnable() {
        @Override
        public void run() {
            try {
                updateTree();
                treeLoaded = true;
            } catch (Exception e) {
                System.out.println("" + e);
            }

        }
    };*/

    public void updateTree() {
        rootModel = treeService.getRootView().getRoot();
        selectedTreeNode = null;
    }

    public void setupTreeNodeDialog(String action) {
        treeNodeTypes = treeService.getTreeTypes();
        this.treeNode = new TreeNodeModelDto();
        if (action.equals("edit")) {
            this.treeNode = treeService.getTreeNode(((TreeNodeModelDto) selectedTreeNode.getData()).getId());
        }
    }

    public void saveTreeNode() {
        if (treeNode.getId() != 0) {
            treeService.saveTreeNode(treeNode, (TreeNodeModelDto) selectedTreeNode.getData());
            ((TreeNodeModelDto) selectedTreeNode.getData()).setName(treeNode.getName());
            selectedTreeNode.setExpanded(true);
        } else {
            treeNode.setPosition((double) selectedTreeNode.getChildCount() + 1);

            long nodeId = treeService.saveTreeNode(treeNode, (TreeNodeModelDto) selectedTreeNode.getData());
            if (nodeId != -1) {
                treeNode.setId(nodeId);
                new DefaultTreeNode(treeNode, selectedTreeNode);
                selectedTreeNode.setExpanded(true);
            }
        }
    }

    public void saveRoleTreeNodes() {
        treeService.saveRoleTreeNodes(selectedTreeNodes, selectedGroupId);
    }

    public void onAdministrationTree() {
        if (administrationActivated) {
            groups = administrationService.getGroups();
        }
    }

    public void onChangeGroup() {
        if (selectedGroupId != 0) {
            rootModel = treeService.getGroupRootView(selectedGroupId).getRoot();
        }
    }

    public void onDragDrop(TreeDragDropEvent event) {
        TreeNodeModelDto nodeData = (TreeNodeModelDto) event.getDragNode().getData();
        double position = this.retrieveNodeInTree(rootModel.getChildren(), event.getDragNode());
        nodeData.setPosition(position);
        nodeData.setMoved(true);
    }

    private double retrieveNodeInTree(List<TreeNode> children, TreeNode draggedNode) {
        double newPos = 1, prev = 0, next;
        for (TreeNode node : children) {
            if (node.getRowKey().equals(draggedNode.getRowKey())) {
                int index = children.indexOf(node);
                if (index == children.size() - 1) {
                    newPos = children.size();
                } else {
                    if (index > 0) {
                        prev = ((TreeNodeModelDto) children.get(index - 1).getData()).getPosition();
                    }
                    next = ((TreeNodeModelDto) children.get(index + 1).getData()).getPosition();
                    newPos = (prev + next) / 2;
                }
                break;
            }
            if (draggedNode.getRowKey().startsWith(node.getRowKey())) {
                newPos = this.retrieveNodeInTree(node.getChildren(), draggedNode);
            }
        }
        return newPos;
    }

    public void onTreeNodeMove() {
        if (!moveActivated) {
            List<TreeNodeModelDto> movedNodes = new ArrayList<>();
            this.collectMovedTreeNodes(rootModel.getChildren(), movedNodes);
            treeService.updateTreeNodesPosition(movedNodes);
        }
    }

    public void collectMovedTreeNodes(List<TreeNode> children, List<TreeNodeModelDto> movedNodes) {
        for (TreeNode node : children) {
            TreeNodeModelDto nodeData = (TreeNodeModelDto) node.getData();
            if (nodeData.hasMoved()) {
                movedNodes.add(nodeData);
            }
            if (node.getChildren().size() > 1) {
                this.collectMovedTreeNodes(node.getChildren(), movedNodes);
            }
        }
    }

    /*public void changeMovedTreeNodesPosition(List<TreeNode> children) {
        for (TreeNode node : children) {
            NodeTreeModelDto nodeData = (NodeTreeModelDto) node.getData();
            if (nodeData.hasMoved()) {
                int index = children.indexOf(node);
                double position = 1, prev = 0, next = 1;

                if (index == children.size() - 1) {
                    position = children.size();
                } else {
                    if (index != 0) {
                        prev = ((NodeTreeModelDto) children.get(index - 1)).getFvr();
                    }
                    next = ((NodeTreeModelDto) children.get(index + 1)).getFvr();
                    position = (prev + next) / 2;
                }
                nodeData.setFvr(position);
            }
            this.changeMovedTreeNodesPosition(node.getChildren());
        }
    }*/


    public TreeNode getRootModel() {
        return rootModel;
    }

    public TreeNode getSelectedTreeNode() {
        return selectedTreeNode;
    }

    public void setSelectedTreeNode(TreeNode selectedTreeNode) {
        this.selectedTreeNode = selectedTreeNode;
    }

    public TreeNode[] getSelectedTreeNodes() {
        return selectedTreeNodes;
    }

    public void setSelectedTreeNodes(TreeNode[] selectedTreeNodes) {
        this.selectedTreeNodes = selectedTreeNodes;
    }

    public boolean isTreeLoaded() {
        return treeLoaded;
    }

    public void setTreeService(TreeService treeService) {
        this.treeService = treeService;
    }

    public boolean isMoveActivated() {
        return moveActivated;
    }

    public void setMoveActivated(boolean moveActivated) {
        this.moveActivated = moveActivated;
    }

    public boolean isAdministrationActivated() {
        return administrationActivated;
    }

    public void setAdministrationActivated(boolean administrationActivated) {
        this.administrationActivated = administrationActivated;
    }

    public TreeNodeModelDto getTreeNode() {
        return treeNode;
    }

    public void setTreeNode(TreeNodeModelDto treeNode) {
        this.treeNode = treeNode;
    }

    public List<ClasificationDto> getTreeNodeTypes() {
        return treeNodeTypes;
    }

    public void setTreeNodeTypes(List<ClasificationDto> treeNodeTypes) {
        this.treeNodeTypes = treeNodeTypes;
    }

    public List<GroupDto> getGroups() {
        return groups;
    }

    public void setGroups(List<GroupDto> groups) {
        this.groups = groups;
    }

    public void setAdministrationService(AdministrationService administrationService) {
        this.administrationService = administrationService;
    }

    public long getSelectedGroupId() {
        return selectedGroupId;
    }

    public void setSelectedGroupId(long selectedGroupId) {
        this.selectedGroupId = selectedGroupId;
    }
}
